﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Carbon.Web.Forms.Extensions;
using System.Web.UI;

namespace Carbon.Web.Forms
{
    public class ViewControlUpdateState
    {
        [Serializable]
        private class EmptyStateMark
        {
            public static EmptyStateMark Instance = new EmptyStateMark();
        }

        private List<EventHandler<ChildViewControlPreUpdateEventArgs>> m_PreUpdateEventHandlers = default(List<EventHandler<ChildViewControlPreUpdateEventArgs>>);
        public List<EventHandler<ChildViewControlPreUpdateEventArgs>> PreUpdateEventHandlers
        {
            get
            {
                return m_PreUpdateEventHandlers;
            }
            set
            {
                m_PreUpdateEventHandlers = value;
            }
        }
        
        private UpdateControlStageEnum m_Stage = default(UpdateControlStageEnum);
        public UpdateControlStageEnum Stage
        {
            get
            {
                return m_Stage;
            }
            set
            {
                m_Stage = value;
            }
        }

        private EventHandler<ControlDescendantsUpdatedEventArgs> m_ControlDescendantsUpdated = default(EventHandler<ControlDescendantsUpdatedEventArgs>);
        public EventHandler<ControlDescendantsUpdatedEventArgs> ControlDescendantsUpdated
        {
            get
            {
                return m_ControlDescendantsUpdated;
            }
            set
            {
                m_ControlDescendantsUpdated = value;
            }
        }

        public static void OnLoadViewState(IViewControl This, object viewState, Action<object> baseLoadViewState)
        {
            if (viewState is EmptyStateMark)
                viewState = null;

            baseLoadViewState(viewState);

            This.PipelineState = new ViewControlUpdateState();
            DoUpdateViewControl(This);
        }

        public static object OnSaveViewState(IViewControl This, Func<object> baseSaveViewState)
        {
            Object viewState = baseSaveViewState();
            
            if (viewState == null)
                viewState = EmptyStateMark.Instance;

            return viewState;
        }

        public static void OnPreRender(IViewControl This, EventArgs args, Action<EventArgs> baseOnPreRender)
        {
            baseOnPreRender(args);
            This.PipelineState = new ViewControlUpdateState();
            DoUpdateViewControl(This);
        }


        /// <summary>
        /// Remove load view state mark
        /// </summary>
        /// <param name="viewState"></param>
        /// <param name="baseLoadViewState"></param>
        internal static void OnLoadViewState(Object viewState, Action<Object> baseLoadViewState)
        {
            if (viewState is EmptyStateMark)
            {
                viewState = null;
            }

            baseLoadViewState(viewState);
        }

        /// <summary>
        /// Ensures view state is loaded on postback.
        /// </summary>
        /// <param name="baseSaveViewState"></param>
        /// <returns></returns>
        internal static object OnSaveViewState(Func<Object> baseSaveViewState)
        {
            Object viewState = baseSaveViewState();
            if (viewState == null)
                viewState = EmptyStateMark.Instance;

            return viewState;
        }

        private static void DoUpdateViewControl(IViewControl This)
        {
            IViewControl parentViewControl = ((Control)This).GetParentControlOrNull<IViewControl>();
            IDataContextControl parentDataContextControl = ((Control)This).GetParentControlOrNull<IDataContextControl>();

            if (!parentDataContextControl.PipelineEnabled)
                return;

            if ((parentViewControl.PipelineState != null) == false) throw new InvalidOperationException("(parentViewControl.PipelineState != null) == false");

            ViewControlUpdateState state = parentViewControl.PipelineState;

            This.PipelineState = new ViewControlUpdateState();

            for (int i = state.PreUpdateEventHandlers.Count - 1; i >= 0; --i)
                state.PreUpdateEventHandlers[i](This, new ChildViewControlPreUpdateEventArgs(This));

            This.PipelineState.PreUpdateEventHandlers = new List<EventHandler<ChildViewControlPreUpdateEventArgs>>(state.PreUpdateEventHandlers);
            UpdateControlEventArgs args = new UpdateControlEventArgs(state.Stage, This.PipelineState.PreUpdateEventHandlers);
            This.OnUpdateControl(This, args);

            This.PipelineState.ControlDescendantsUpdated = args.ControlDescendantsUpdated;
        }
    }

    public interface IViewControl
    {
        void OnUpdateControl(object sender, UpdateControlEventArgs args);
        void OnUpdateModel(object sender, UpdateModelEventArgs args);
        
        // used internally
        ViewControlUpdateState PipelineState { get; set; }
    }
}
